package com.artemis.utils;

/**
 * A Pool of objects that can be reused to avoid allocation. <br><br>
 * Inspired by libgdx's (see: http://github.com/libgdx/libgdx) implementation.
 *
 * @param <E> type of the objects it holds. 
 *  
 * @author kryo
 */
public abstract class Pool<E> {
	private final Bag<E> freeObjects;
	
	/** 
	 * The maximum amount of objects that will be pooled. 
	 */
	public final int max;
	
	/**
	 * Creates a pool with an initial capacity of 16 and no maximum.
	 */
	public Pool() {
		this(16);
	}
	
	/**
	 * Creates a pool with a specific initial capacity and no maximum.
	 * 
	 * @param initialCapacity the initial capacity of the pool.
	 */
	public Pool(int initialCapacity) {
		this(initialCapacity, Integer.MAX_VALUE);
	}
	
	/**
	 * Creates a pool with a specific initial capacity and maximum.
	 * 
	 * @param initialCapacity the initial capacity of the pool.
	 * @param max the maximum amount of objects that will be pooled.
	 */
	public Pool(int initialCapacity, int max) {
		freeObjects = new Bag<E>(initialCapacity);
		this.max = max;
	}
	
	/**
	 * Instantiates a new object.
	 * 
	 * @return the new object.
	 */
	public abstract E newObject();
	
	/**
	 * Returns an object from this pool. The object may be new ({@link #newObject()}) or 
	 * reused (via {@link #free(Object)} previously freed).
	 * 
	 * @return an object from this pool.
	 */
	public E obtain() {
		return freeObjects.size() == 0 ? newObject() : freeObjects.removeLast();
	}
	
	/**
	 * Put an object back to the pool if you no longer need it.
	 * If the object is an instance of {@link Poolable}, it will
	 * automatically {@link Poolable#reset() reseted}.
	 * 
	 * @param object the object that will put into this pool.
	 */
	public void free(E object) {
		if(object == null) return;
		if(freeObjects.size() < max) freeObjects.add(object);
		if(object instanceof Poolable) ((Poolable) object).reset();
	}
	
	/**
	 * The amount of objects that are stored on this pool.
	 * 
	 * @return the size of the pool.
	 */
	public int size() {
		return freeObjects.size();
	}
	
	/**
	 * Removes all objects that are stored on this pool.
	 */
	public void clear () {
		freeObjects.clear();
	}
	
	/**
	 * Reduces the size of the backing array.
	 */
	public void shrink() {
		freeObjects.shrink();
	}
	
	/**
	 * If an object implements this interface, it will automatically
	 * reseted (via {@link #reset()}) when it is freed.
	 * 
	 * @author kryo
	 *
	 * @see Pool#free(Object)
	 */
	public static interface Poolable {
		
		/**
		 * Set your fields back to their default values here.
		 */
		public void reset();
	}
}
